#ifndef _OBJECT_POOL_H_
#define _OBJECT_POOL_H_

#include <queue>
#include <vector>
#include <stdexcept>
#include <memory>
#include <thread>
#include <atomic>
#include <map>
#include <utility>
#if (defined _WIN64 ) || (defined _MSC_VER) 
#include <windows.h>
#include <windef.h>
#endif

using std::queue;
using std::vector;

//
// template class ObjectPool
//
// Provides an object pool that can be used with any class that provides a
// default constructor.
//
// The object pool constructor creates a pool of objects, which it hands out
// to clients when requested via the acquireObject() method. When a client is
// finished with the object it calls releaseObject() to put the object back
// into the object pool.
//
// The constructor and destructor on each object in the pool will be called only
// once each for the lifetime of the program, not once per acquisition and release.
//
// The primary use of an object pool is to avoid creating and deleting objects
// repeatedly. The object pool is most suited to applications that use large 
// numbers of objects for short periods of time.
//
// For efficiency, the object pool doesn't perform sanity checks.
// Expects the user to release every acquired object exactly once.
// Expects the user to avoid using any objects that he or she has released.
//
// Expects the user not to delete the object pool until every object
// that was acquired has been released. Deleting the object pool invalidates
// any objects that the user had acquired, even if they had not yet been released.
//



template <typename T>
class ObjectPool
{
 public:
  //
  // Creates an object pool with chunkSize objects.
  // Whenever the object pool runs out of objects, chunkSize
  // more objects will be added to the pool. The pool only grows:
  // objects are never removed from the pool (freed), until
  // the pool is destroyed.
  //
  // Throws invalid_argument if chunkSize is <= 0.
  //
	 ObjectPool();
	 ObjectPool(int chunkSize = kDefaultChunkSize)
		 throw(std::invalid_argument, std::bad_alloc);

  //
  // Frees all the allocated objects. Invalidates any objects that have
  // been acquired for use.
  //
	 ~ObjectPool();

  //
  // Reserve an object for use. The reference to the object is invalidated
  // if the object pool itself is freed.
  // 
  // Clients must not free the object!
  //
	 T& acquireObject();

  //
  // Return the object to the pool. Clients must not use the object after
  // it has been returned to the pool.
  //
	 void releaseObject(T& obj);

 protected:
  //
  // mFreeList stores the objects that are not currently in use
  // by clients.
  //
	 queue<T*> mFreeList;
  //
  // mAllObjects stores pointers to all the objects, in use
  // or not. This vector is needed in order to ensure that all
  // objects are freed properly in the destructor.
  //
	 vector<T*> mAllObjects;
	 
	 int mChunkSize;
	 
	 static const int kDefaultChunkSize = 10;

  //
  // Allocates mChunkSize new objects and adds them
  // to the mFreeList.
  //
	 void allocateChunk();
	 static void arrayDeleteObject(T* obj);

 private:
  // Prevent assignment and pass-by-value
	 ObjectPool(const ObjectPool<T>& src);
	 ObjectPool<T>& operator=(const ObjectPool<T>& rhs);
};

template<typename T>
const int ObjectPool<T>::kDefaultChunkSize;

template <typename T>
ObjectPool<T>::ObjectPool(int chunkSize) throw(std::invalid_argument,
    std::bad_alloc) : mChunkSize(chunkSize)
{
    if (mChunkSize <= 0) {
        throw std::invalid_argument("chunk size must be positive");
    }
    // Create mChunkSize objects to start
    allocateChunk();
}

//
// Allocates an array of mChunkSize objects because that's
// more efficient than allocating each of them individually.
// Stores a pointer to the first element of the array in the mAllObjects
// vector. Adds a pointer to each new object to the mFreeList.
//
template <typename T>
void ObjectPool<T>::allocateChunk()
{
	T* newObjects = new T[mChunkSize];
    mAllObjects.push_back(newObjects);
    for (int i = 0; i < mChunkSize; i++) {
        mFreeList.push(&newObjects[i]);
    }
}

//
// Freeing function for use in the for_each algorithm in the
// destructor.
//
template<typename T>
void ObjectPool<T>::arrayDeleteObject(T* obj)
{
    delete [] obj;
}

template <typename T>
ObjectPool<T>::~ObjectPool()
{
    // free each of the allocation chunks
    for_each(mAllObjects.begin(), mAllObjects.end(), arrayDeleteObject);
}

template <typename T>
T& ObjectPool<T>::acquireObject()
{
    if (mFreeList.empty()) {
        allocateChunk();
    }
    T* obj = mFreeList.front();
    mFreeList.pop();
    return (*obj);
}

template <typename T>
void ObjectPool<T>::releaseObject(T& obj)
{
    mFreeList.push(&obj);
}


#endif // !_OBJECT_POOL_H_

